/*
 * Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

package org.geometerplus.fbreader.book;


import com.example.administrator.fangbook.appliction.HtlApplication;

import org.geometerplus.fbreader.Paths;
import org.geometerplus.fbreader.filetype.FileType17KTxtNovel;
import org.geometerplus.fbreader.filetype.FileTypeTxt;
import org.geometerplus.fbreader.formats.BookReader;
import org.geometerplus.fbreader.formats.BookReadingException;
import org.geometerplus.fbreader.formats.FormatPlugin;
import org.geometerplus.fbreader.formats.PluginCollection;
import org.geometerplus.fbreader.library.Author;
import org.geometerplus.fbreader.library.BooksDatabase;
import org.geometerplus.zlibrary.core.filesystem.ZL17KPlainTxtFile;
import org.geometerplus.zlibrary.core.filesystem.ZLFile;
import org.geometerplus.zlibrary.core.filesystem.ZLPhysicalFile;
import org.geometerplus.zlibrary.core.filesystem.ZLTxtFile;
import org.geometerplus.zlibrary.core.util.MiscUtil;
import org.geometerplus.zlibrary.text.view.ZLTextPosition;

import java.util.ArrayList;
import java.util.Set;
import java.util.TreeSet;

/**
 * 保存书籍实体类
 */
public class ReadBook extends AbstractBook {
    public final ZLFile File;
    protected BookReader myBookReader;
    private Set<String> myVisitedHyperlinks;

    public ReadBook(long id, ZLFile file, String title, String encoding, String language) {
        super(id, title, encoding, language);
        if (file == null) {
            throw new IllegalArgumentException("Creating book with no file");
        }
        File = file;
    }

    public ReadBook(ZLFile file, FormatPlugin plugin) throws BookReadingException {
        this(-1, plugin.realBookFile(file), null, null, null);
        BookUtil.readMetainfo(this, plugin);
        myIsSaved = false;
    }

    @Override
    public String getPath() {
        return File.getPath();
    }

    public void loadLists(BooksDatabase database) {
        myAuthors = database.loadAuthors(myId);
        myTags = database.loadTags(myId);
        mySeriesInfo = database.loadSeriesInfo(myId);
        myProgress = null;
        myIsSaved = true;

    }

    boolean save(final BooksDatabase database, boolean force) {
        if (!force && myId != -1 && myIsSaved) {
            return false;
        }

        final boolean[] result = new boolean[]{true};
        database.executeAsATransaction(new Runnable() {
            public void run() {
                if (myId >= 0) {
                    final FileInfoSet fileInfos = new FileInfoSet(database, File);
                    database.updateBookInfo(myId, fileInfos.getId(File), myEncoding, myLanguage, getTitle());
                } else {
                    if (ReadBook.this instanceof M17kPlainTxtBook) {
                        M17kPlainTxtBook book = (M17kPlainTxtBook) ReadBook.this;
                        myId = database.insertBookInfo(File, myEncoding, myLanguage, getTitle(), FileType17KTxtNovel.FileMode + book.getBookId());
                    } else if (ReadBook.this instanceof MTxtBook) {
                        MTxtBook book = (MTxtBook) ReadBook.this;
                        if (getTitle() == null) {
                            setTitle(book.getTitle());
                        }
                        if (getTitle() == null) {
                            setTitle(book.getShelfBook().getName());
                        }
                        myId = database.insertBookInfo(File, myEncoding, myLanguage, getTitle(), FileTypeTxt.FileMode + book.getBookId());
                    } else {
                        myId = database.insertBookInfo(File, myEncoding, myLanguage, getTitle(), "");
                    }
                    //storeAllVisitedHyperinks();
                }

                long index = 0;
                database.deleteAllBookAuthors(myId);
                for (Author author : authors()) {
                    database.saveBookAuthorInfo(myId, index++, author);
                }
                database.deleteAllBookTags(myId);
                for (Tag tag : tags()) {
                    database.saveBookTagInfo(myId, tag);
                }

                database.saveBookSeriesInfo(myId, mySeriesInfo);

            }
        });

        if (result[0]) {
            myIsSaved = true;
            return true;
        } else {
            return false;
        }
    }

    private void initHyperlinkSet(BooksDatabase database) {
        if (myVisitedHyperlinks == null) {
            myVisitedHyperlinks = new TreeSet<String>();
            if (myId != -1) {
                myVisitedHyperlinks.addAll(database.loadVisitedHyperlinks(myId));
            }
        }
    }

    public boolean isHyperlinkVisited(BooksDatabase database, String linkId) {
        initHyperlinkSet(database);
        return myVisitedHyperlinks.contains(linkId);
    }

    void markHyperlinkAsVisited(BooksDatabase database, String linkId) {
        initHyperlinkSet(database);
        if (!myVisitedHyperlinks.contains(linkId)) {
            myVisitedHyperlinks.add(linkId);
            if (myId != -1) {
                database.addVisitedHyperlink(myId, linkId);
            }
        }
    }

    boolean hasSameMetainfoAs(ReadBook other) {
        return
                MiscUtil.equals(getTitle(), other.getTitle()) &&
                        MiscUtil.equals(myEncoding, other.myEncoding) &&
                        MiscUtil.equals(myLanguage, other.myLanguage) &&
                        MiscUtil.equals(myAuthors, other.myAuthors) &&
                        MiscUtil.listsEquals(myTags, other.myTags) &&
                        MiscUtil.equals(mySeriesInfo, other.mySeriesInfo);
    }

    void merge(ReadBook other, ReadBook base) {
        if (!MiscUtil.equals(getTitle(), other.getTitle()) &&
                MiscUtil.equals(getTitle(), base.getTitle())) {
            setTitle(other.getTitle());
        }
        if (!MiscUtil.equals(myEncoding, other.myEncoding) &&
                MiscUtil.equals(myEncoding, base.myEncoding)) {
            setEncoding(other.myEncoding);
        }
        if (!MiscUtil.equals(myLanguage, other.myLanguage) &&
                MiscUtil.equals(myLanguage, base.myLanguage)) {
            setLanguage(other.myLanguage);
        }
        if (!MiscUtil.listsEquals(myTags, other.myTags) &&
                MiscUtil.listsEquals(myTags, base.myTags)) {
            myTags = other.myTags != null ? new ArrayList<Tag>(other.myTags) : null;
            myIsSaved = false;
        }
        if (!MiscUtil.equals(mySeriesInfo, other.mySeriesInfo) &&
                MiscUtil.equals(mySeriesInfo, base.mySeriesInfo)) {
            mySeriesInfo = other.mySeriesInfo;
            myIsSaved = false;
        }

    }

    @Override
    public int hashCode() {
        return File.getShortName().hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof ReadBook)) {
            return false;
        }
        final ReadBook obook = ((ReadBook) o);
        final ZLFile ofile = obook.File;
        if (File.equals(ofile)) {
            return true;
        }
        if (!File.getShortName().equals(ofile.getShortName())) {
            return false;
        }

        return true;
    }

    public ZLTextPosition getStoredPosition() {
        return BooksDatabase.Instance().getStoredPosition(myId);
    }

    public void storePosition(ZLTextPosition position) {
        if (myId != -1 && position != null) {
            BooksDatabase.Instance().storePosition(myId, position);
        }
    }

    public void reloadInfoFromDatabase() {
        final BooksDatabase database = BooksDatabase.Instance();
        database.reloadBook(this);
        myAuthors = database.loadAuthors(myId);
        myTags = database.loadTags(myId);
        mySeriesInfo = database.loadSeriesInfo(myId);
        myIsSaved = true;
    }


    public void insertIntoBookList() {
        if (myId != -1) {
            BooksDatabase.Instance().insertIntoBookList(myId);
        }
    }

    public static ReadBook getByFile(ZLFile bookFile) {
        try {
            PluginCollection pCollection = PluginCollection.Instance(Paths.systemInfo(HtlApplication.getApplication()));
            FormatPlugin plugin = null;
            if (pCollection != null) {
                plugin = pCollection.getPlugin(bookFile);
            }
            return getByFile(bookFile, plugin);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static ReadBook getByFile(ZLFile bookFile, FormatPlugin plugin) throws BookReadingException {
        if (bookFile == null) {
            return null;
        }


        final ZLPhysicalFile physicalFile = bookFile.getPhysicalFile();
        if (physicalFile != null && !physicalFile.exists()) {
            return null;
        }
        //数据库所有的内容提取
        final FileInfoSet fileInfos = new FileInfoSet(BooksDatabase.Instance(), bookFile);
        //查询数据库创建书籍信息
        ReadBook book = BooksDatabase.Instance().loadBookByFile(fileInfos.getId(bookFile), bookFile);
        if (book != null) {
            book.loadLists(BooksDatabase.Instance());
        }

        if (book != null && fileInfos.check(physicalFile, physicalFile != bookFile)) {
            return book;
        }
        //移除书籍存储
        fileInfos.save();

        if (book == null) {
            if (bookFile instanceof ZL17KPlainTxtFile) {
                book = new M17kPlainTxtBook((ZL17KPlainTxtFile) bookFile, plugin);
            } else if (bookFile instanceof ZLTxtFile) {
                book = new MTxtBook((ZLTxtFile) bookFile, plugin);
            } else {
                book = new ReadBook(bookFile, plugin);
            }
        }
        if (plugin != null && book.readMetaInfo(plugin)) {
            book.save();
            return book;
        }
        return null;
    }

    boolean readMetaInfo(FormatPlugin plugin) {
        myEncoding = null;
        myLanguage = null;
        setTitle(null);
        myAuthors = null;
        myTags = null;
        mySeriesInfo = null;

        myIsSaved = false;


        try {
            plugin.readMetainfo(this);
        } catch (BookReadingException e) {
            e.printStackTrace();
        }
        if (getTitle() == null || getTitle().length() == 0) {
            final String fileName = File.getShortName();
            final int index = fileName != null ? fileName.lastIndexOf('.') : 0;
            setTitle(index > 0 ? fileName.substring(0, index) : fileName);
        }

        return true;
    }

    public boolean save() {
        if (myIsSaved) {
            return false;
        }
        final BooksDatabase database = BooksDatabase.Instance();
        database.executeAsATransaction(new Runnable() {
            public void run() {
                if (myId >= 0) {
                    final FileInfoSet fileInfos = new FileInfoSet(BooksDatabase.Instance(), File);
                    database.updateBookInfo(myId, fileInfos.getId(File), myEncoding, myLanguage, getTitle());
                } else {
                    if (ReadBook.this instanceof M17kPlainTxtBook) {
                        M17kPlainTxtBook book = (M17kPlainTxtBook) ReadBook.this;
                        myId = database.insertBookInfo(File, myEncoding, myLanguage, getTitle(), FileType17KTxtNovel.FileMode + book.getBookId());
                    } else if (ReadBook.this instanceof MTxtBook) {
                        MTxtBook book = (MTxtBook) ReadBook.this;
                        if (getTitle() == null) {
                            setTitle(book.getTitle());
                        }
                        if (getTitle() == null) {
                            setTitle(book.getShelfBook().getName());
                        }
                        myId = database.insertBookInfo(File, myEncoding, myLanguage, getTitle(), FileTypeTxt.FileMode + book.getBookId());
                    } else {
                        myId = database.insertBookInfo(File, myEncoding, myLanguage, getTitle(), "");
                    }
                    // storeAllVisitedHyperinks();
                }

                long index = 0;
                database.deleteAllBookAuthors(myId);
                for (Author author : authors()) {
                    database.saveBookAuthorInfo(myId, index++, author);
                }
                database.deleteAllBookTags(myId);
                for (Tag tag : tags()) {
                    database.saveBookTagInfo(myId, tag);
                }
                database.saveBookSeriesInfo(myId, mySeriesInfo);
            }
        });

        myIsSaved = true;
        return true;
    }

    public void setBookReader(BookReader reader) {
        myBookReader = reader;
    }

    public BookReader getReader() {
        return myBookReader;
    }
}
